package dispatcher.group.gui.network;

import java.io.IOException;
import java.net.InetAddress;

import dispatcher.gui.event.StatusInitializeThread;
import dispatcher.gui.event.StatusUpdateThreadPool;

import messages.BaseMessage;
import messages.DeregistrationMessage;
import messages.MessageFactory;
import messages.MessageType;
import messages.observer.CurrentStateMessage;
import messages.observer.CurrentStateRequest;

import observer.ObserverThread;
import state.Globals;

/**
 * This class is handles all connections to the group dispatcher. Once connected to 
 * the group dispatcher it will then query the whole group dispatcher state. Once the
 * state has been retrieved, it will then wait for any new event to arrive. The only message
 * that this thread can send to the group dispatcher is the ability to kill an elevator.
 * @author Caleb
 *
 */
public class GroupDispatcherGUIObserverThread extends ObserverThread{

	
	public GroupDispatcherGUIObserverThread(InetAddress remoteAddr,	int remotePort) {
		super(remoteAddr, remotePort);
	}

	/**
	 * This method will attempt to reconnect to the GroupDispatcher server. It will attempt to reconnect the socket
	 * and will then reinitialize all the elevator and floor data
	 * @deprecated Does not restart the thread
	 */
	public void reconnectToServer(){
		// Initialise the socket
		if (!init()){
            if (getRemoteSocket() != null){
                try{
                	getRemoteSocket().close();
                }catch (IOException e){
                    Globals.getLogger().error("Could not close socket after failed reconnect", e);
                }
            }
            Globals.getLogger().log("Could not initiate a connection, shutting down thread", this.getClass().getName(), "reconnectToServer");
            return;
        }
		// Call the pre-run conditions
		if (!onStart())
        {
        	try
        	{
        		getRemoteSocket().close();
        	}
        	catch(IOException e)
        	{
        	    Globals.getLogger().error("Could not close socket after failed onStart", e);
        	}
        	Globals.getLogger().log("onStart failed, shutting down thread", this.getClass().getName(), "run");
        }
	}
	
	/**
	 * This method will run the pre-run conditions needed before the thread starts running
	 */
	public boolean onStart(){
		Globals.getLogger().log("Start", this.getClass().getName(), "onStart");
		// Request the current state of the GroupDispatcher
		CurrentStateRequest message = new CurrentStateRequest();
		try
        {
			getRemoteSocket().getOutputStream().write(message.toByteArray());
        }
        catch (IOException e)
        {
        	Globals.getLogger().error("Could not send CurrentStateRequest message", e);
        	Globals.getLogger().log("Could not send CurrentStateRequest message, shutting down thread", this.getClass().getName(), "onStart");
            return false;
        }
		
		// Waits for the return message
		BaseMessage returnMess = null;
        try
        {
        	returnMess = MessageFactory.readMessage(getRemoteSocket());
        }
        catch (IOException e)
        {
        	Globals.getLogger().error("Could not read CurrentStateMesage message", e);
        	Globals.getLogger().log("Could not read CurrentStateMesage message, shutting down thread", this.getClass().getName(), "onStart");
            return false;
        }
		// Checks if the return message is of the right type
        if (returnMess.getMessageType() == MessageType.CurrentStateMessage){
        	
        	CurrentStateMessage mess = (CurrentStateMessage)returnMess;
        	
        	try {
        		// Creates a request to initialise the GroupDispatcherStateHandler
				(new StatusInitializeThread(mess.getDispatcherState())).start();
			} catch (IOException e) {
				Globals.getLogger().error("Could not parse DispatcherState", e);
	        	Globals.getLogger().log("Could not parse DispatcherState, shutting down thread", this.getClass().getName(), "onStart");
				return false;
			}
        	Globals.getLogger().log("Start Finished", this.getClass().getName(), "onStart");
        	return true;
        }
        
    	Globals.getLogger().log("Message was not CurrentStateMessage", this.getClass().getName(), "onStart");
		return false;
	}
	
	/**
	 * This method will send the kill elevator message to the group dispatcher
	 * @param message The kill message
	 * @return Return true if message was send successfully
	 */
	public synchronized boolean sendKillMessage(BaseMessage message){
		try{
			getRemoteSocket().getOutputStream().write(message.toByteArray());
		}catch(IOException e){
			Globals.getLogger().error("Could not send BaseMessage message", e);
        	Globals.getLogger().log("Could not send BaseMessage message, shutting down thread", this.getClass().getName(), "sendPersonMessage");
        	return false;
		}catch(NullPointerException e){
			Globals.getLogger().error("Could not open port", e);
        	Globals.getLogger().log("Could not open port, shutting down thread", this.getClass().getName(), "sendPersonMessage");
        	return false;
		}
		Globals.getLogger().log("Kill message sent", this.getClass().getName(), "sendKillMessage");
		return true;
	}
	
	public synchronized boolean sendTerminationMessage(){
		DeregistrationMessage message = new DeregistrationMessage();
		try{
			getRemoteSocket().getOutputStream().write(message.toByteArray());
		}catch(IOException e){
			Globals.getLogger().error("Could not send BaseMessage message", e);
        	Globals.getLogger().log("Could not send BaseMessage message, shutting down thread", this.getClass().getName(), "sendTerminationMessage");
        	return false;
		}catch(NullPointerException e){
			Globals.getLogger().error("Could not open port", e);
        	Globals.getLogger().log("Could not open port, shutting down thread", this.getClass().getName(), "sendTerminationMessage");
        	return false;
		}
		Globals.getLogger().log("Deregister message sent", this.getClass().getName(), "sendTerminationMessage");
		StatusUpdateThreadPool.getInstance().terminate();
		return true;
	}
}
